package com.abo.yaas.webservice.hibernate;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;

import com.abo.yaas.webservice.constants.Operation;

public class WSFilter<T> {
    private String property = null;

    private Object value = null;

    private Operation op = Operation.NOP;

    private final List<WSFilter<T>> children = new ArrayList<WSFilter<T>>();

    private EntityType<T> entityType;

    private final EntityManager em = EntityManagerUtils.getEntityManager();

    private CriteriaBuilder builder;

    public WSFilter() {
        builder = em.getCriteriaBuilder();
    }

    public WSFilter(String property, Object value, Operation op, Class<T> classType) {
        this();
        this.property = property;
        this.value = value;
        this.op = op;
        entityType = em.getMetamodel().entity(classType);
    }

    public void addChild(final WSFilter<T> filter) {
        children.add(filter);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Predicate getPredicate(Root root) {
        Predicate pred = null;
        if (!op.equals(Operation.NOP)) {
            final SingularAttribute<T, ?> attr = (SingularAttribute<T, ?>) entityType
                    .getAttribute(property);
            switch (op) {
            case LIKE:
                pred = builder.like(root.get(attr), "%" + value.toString() + "%");
                break;
            case EQ:
                pred = builder.equal(root.get(attr), value);
                break;
            case GT:
                pred = builder.gt((Expression<Number>) root.get(attr),
                        (Number) value);
                break;
            case LT:
                pred = builder.lt((Expression<Number>) root.get(attr),
                        (Number) value);
                break;
            }
        }
        if (!children.isEmpty()) {
            for (final WSFilter<T> filter : children) {
                if (pred == null) {
                    pred = filter.getPredicate(root);
                } else {
                    pred = builder.and(pred, filter.getPredicate(root));
                }
            }
        }
        return pred;
    }
}
